﻿/* Copyright (c) 2009, 2010, John Cronin
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holder nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

using System;
using System.Collections.Generic;
using System.Text;

namespace tysila
{
    class Mangler
    {
        public static bool IncludeModule = true;

        public static string Mangle(Metadata.MethodDefRow mdr, Assembler ass)
        {
            return Mangle(mdr, Signature.ParseMethodDefSig(mdr.m, mdr.Signature), ass);
        }

        public static string MethodOnlyMangle(Metadata.MethodDefRow mdr, Assembler ass)
        {
            return MethodOnlyMangle(mdr, Signature.ParseMethodDefSig(mdr.m, mdr.Signature), ass);
        }

        public static string Mangle(IEnumerable<byte> sig)
        {
            StringBuilder sb = new StringBuilder();
            foreach (byte b in sig)
                sb.Append(b.ToString("X2"));
            return "_S" + sb.ToString().Length + "_" + sb.ToString();
        }

        public static string Mangle(Signature.Param sig, Assembler ass)
        {
            string s = BaseMangle(sig, ass);
            return "_S" + s.Length.ToString() + "_" + s;
        }

        public static string Mangle(Signature.Field sig, Assembler ass)
        {
            string s = BaseMangle(sig, ass);
            return "_S" + s.Length.ToString() + "_" + s;
        }

        public static string BaseMangle(Signature.Field sig, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("06");
            sb.Append(BaseMangle(sig.AsParam(ass), ass));

            return sb.ToString();
        }

        public static string BaseMangle(Signature.Param sig, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();

            foreach (Signature.CustomMod cm in sig.CustomMods)
                sb.Append(BaseMangle(cm));
            if (sig.TypedByRef)
                sb.Append("16");
            else
            {
                if (sig.ByRef)
                    sb.Append("10");
                sb.Append(BaseMangle(sig.Type, ass));
            }

            return sb.ToString();
        }

        public static string BaseMangle(Signature.BaseOrComplexType bct, Assembler ass)
        {
            if (bct is Signature.BaseType)
                return BaseMangle(bct as Signature.BaseType);
            else if (bct is Signature.ComplexType)
                return BaseMangle(bct as Signature.ComplexType, ass);
            else if (bct is Signature.ZeroBasedArray)
                return BaseMangle(bct as Signature.ZeroBasedArray, ass);
            else if (bct is Signature.GenericType)
                return BaseMangle(bct as Signature.GenericType, ass);
            else if (bct is Signature.BoxedType)
                return "51" + BaseMangle(((Signature.BoxedType)bct).Type, ass);
            else if (bct is Signature.ManagedPointer)
                return "0F" + BaseMangle(((Signature.ManagedPointer)bct).ElemType, ass);
            else
                throw new NotImplementedException();
        }

        public static string BaseMangle(Signature.GenericType gt, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("15");
            sb.Append(BaseMangle(gt.GenType, ass));
            sb.Append(gt.GenParams.Count.ToString("X2"));
            foreach (Signature.BaseOrComplexType gp in gt.GenParams)
                sb.Append(BaseMangle(gp, ass));

            return sb.ToString();
        }

        public static string BaseMangle(Signature.ZeroBasedArray zba, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("1D");
            sb.Append(BaseMangle(zba.ElemType, ass));

            return sb.ToString();
        }

        public static string BaseMangle(Signature.BaseType bt)
        {
            return ((byte)((int)bt.Type)).ToString("X2");
        }

        public static string BaseMangle(Signature.ComplexType ct, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();

            if (ct.IsValueType)
                sb.Append("11");
            else
                sb.Append("12");
            Metadata.TypeDefRow tdr = Metadata.GetTypeDef(ct, ass);
            byte[] ci = Metadata.CompressInteger(Convert.ToUInt32(new Metadata.TableIndex(tdr.m, tdr).ToCodedIndex(Metadata.TableIndex.TypeDefOrRef)));
            foreach (byte b in ci)
                sb.Append(b.ToString("X2"));

            return sb.ToString();
        }

        public static string BaseMangle(Signature.CustomMod cm)
        {
            StringBuilder sb = new StringBuilder();

            if (cm.TypeOfCustomMod == Signature.CustomMod.CustomModType.Optional)
                sb.Append("20");
            else
                sb.Append("1F");
            sb.Append(Metadata.CompressInteger(Convert.ToUInt32(cm.Type.ToCodedIndex(Metadata.TableIndex.TypeDefOrRef))));

            return sb.ToString();
        }

        public static string BaseMangle(Signature.Method meth, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();
            List<byte> bret = new List<byte>();
            byte b1 = 0x00;
            if (meth.HasThis)
                b1 |= 0x20;
            if (meth.ExplicitThis)
                b1 |= 0x40;
            if (meth.CallingConvention == Signature.Method.CallConv.Generic)
                b1 |= 0x10;
            else if (meth.CallingConvention == Signature.Method.CallConv.VarArg)
                b1 |= 0x05;
            bret.Add(b1);
            if(meth.CallingConvention == Signature.Method.CallConv.Generic)
                bret.AddRange(Metadata.CompressInteger((uint)meth.GenParamCount));
            bret.AddRange(Metadata.CompressInteger((uint)meth.Params.Count));

            foreach (byte b in bret)
                sb.Append(b.ToString("X2"));

            sb.Append(BaseMangle(meth.RetType, ass));
            foreach (Signature.Param p in meth.Params)
                sb.Append(BaseMangle(p, ass));

            return sb.ToString();
        }

        public static string BaseMangle(Signature.GenericMethod meth, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();
            List<byte> bret = new List<byte>();
            bret.Add(0x0a);
            bret.AddRange(Metadata.CompressInteger((uint)meth.GenParams.Count));

            foreach (byte b in bret)
                sb.Append(b.ToString("X2"));

            foreach (Signature.BaseOrComplexType p in meth.GenParams)
                sb.Append(BaseMangle(p, ass));

            return sb.ToString();
        }

        public static string BaseMangle(Signature.BaseMethod bm, Assembler ass)
        {
            if (bm is Signature.Method)
                return BaseMangle(bm as Signature.Method, ass);
            else if (bm is Signature.GenericMethod)
                return BaseMangle(bm as Signature.GenericMethod, ass);
            else
                throw new NotSupportedException();
        }

        public static string MethodOnlyMangle(Metadata.MethodDefRow mdr, Signature.BaseMethod bmeth, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("_M");
            sb.Append(mdr.Name.Length);
            sb.Append("_");
            sb.Append(mdr.Name);
            string bm = BaseMangle(bmeth, ass);
            sb.Append("_S" + bm.Length.ToString() + "_");
            sb.Append(bm);

            return sb.ToString();
        }

        public static string Mangle(Metadata.TypeDefRow tdr, Signature.Param tsig,
            Metadata.MethodDefRow mdr, Signature.BaseMethod bmeth, Assembler ass)
        { return Mangle(tdr, tsig, ass) + MethodOnlyMangle(mdr, bmeth, ass); }

        public static string Mangle(Assembler.MethodToCompile mtc, Assembler ass)
        { return Mangle(mtc.type, new Signature.Param(ass) { Type = mtc.tsig }, mtc.meth, mtc.msig, ass); }

        public static string Mangle(Metadata.MethodDefRow mdr, Signature.BaseMethod bmeth, Assembler ass)
        {
            // Mangle a method name
            // Output is: namespace::type::method::sig

            Metadata.TypeDefRow tdr = Metadata.GetOwningType(mdr.m, mdr);
            Signature.Param tsig = new Signature.Param(new Token(tdr), ass);
            return Mangle(tdr, tsig, mdr, bmeth, ass);
        }

        public static string FieldOnlyMangle(Metadata.FieldRow fr, Signature.Field fsig, Assembler ass)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("_F");
            sb.Append(fr.Name.Length);
            sb.Append("_");
            sb.Append(fr.Name);
            sb.Append(Mangle(fsig, ass));

            return sb.ToString();
        }
        
        

        public static string FieldOnlyMangle(Metadata.FieldRow fr)
        {
            throw new NotImplementedException("Use Mangle(Assembler.FieldToCompile)");
            /*StringBuilder sb = new StringBuilder();
            sb.Append("_F");
            sb.Append(fr.Name.Length);
            sb.Append("_");
            sb.Append(fr.Name);
            sb.Append(Mangle(fr.Signature));
            return sb.ToString();*/
        }

        public static string Mangle(Metadata.FieldRow fr, Signature.BaseOrComplexType sig, Assembler ass)
        {
            // Mangle a field name
            // Output is: namespace::type::field::sig

            Metadata.TypeDefRow tdr = Metadata.GetOwningType(fr.m, fr);
            Signature.Param tsig = new Signature.Param(new Token(tdr), ass);

            return Mangle(tdr, tsig, ass) + FieldOnlyMangle(fr);
        }

        public static string Mangle(Assembler.TypeToCompile ttc, Assembler ass)
        { return Mangle(ttc.type, ttc.tsig, ass); }

        public static string Mangle(Metadata.ITableRow trow, Assembler ass)
        {
            if (trow is Metadata.TypeDefRow)
                return Mangle(trow as Metadata.TypeDefRow, ass);
            throw new NotSupportedException();
        }

        public static string Mangle(Metadata m)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("_m");
            sb.Append(m.ModuleName.Length);
            sb.Append("_");
            sb.Append(m.ModuleName);
            return sb.ToString();
        }

        public static string Mangle(Metadata.TypeDefRow tdr, Signature.Param sig, Assembler ass)
        {
            if ((tdr == null) && (ass != null))
            {
                tdr = Metadata.GetTypeDef(sig.Type, ass);
            }

            if (sig.Type is Signature.GenericType)
                tdr = Metadata.GetTypeDef(((Signature.GenericType)sig.Type).GenType, ass);

            StringBuilder sb = new StringBuilder();
            if(IncludeModule)
                sb.Append(Mangle(tdr.m));
            sb.Append("_N");
            sb.Append(tdr.TypeNamespace.Length);
            sb.Append("_");
            sb.Append(tdr.TypeNamespace);
            sb.Append("_T");
            sb.Append(tdr.TypeName.Length);
            sb.Append("_");
            sb.Append(tdr.TypeName);

            if (Metadata.IsSpecialType(tdr, sig.Type))
                sb.Append("_Special");
            else
            {
                string sigm = BaseMangle(sig, ass);
                sb.Append("_S");
                sb.Append(sigm.Length.ToString());
                sb.Append("_");
                sb.Append(BaseMangle(sig, ass));
            }

            return sb.ToString();
        }

        public static string Mangle(Assembler.FieldToCompile ftc, Assembler ass)
        {
            //return Mangler.Mangle(ftc.memberof_type, ftc.memberof_tsig.Type) + Mangler.FieldOnlyMangle(ftc.field, ftc.fsig);
            return FieldOnlyMangle(ftc.field, new Signature.Field(ftc.fsig), ass);
        }

        public static Assembler.TypeToCompile DemangleType(string s, Assembler ass)
        {
            Dictionary<string, string> tokens = _SplitMangledName(s);
            return _DemangleType(tokens, ass);
        }

        public static Assembler.MethodToCompile DemangleMethod(string s, Assembler ass)
        {
            Dictionary<string, string> tokens = _SplitMangledName(s);
            Assembler.TypeToCompile ttc = _DemangleType(tokens, ass);
            Assembler.MethodToCompile mtc = new Assembler.MethodToCompile();
            mtc.tsig = ttc.tsig.Type;
            mtc.type = ttc.type;
            int x = 0;
            Signature.BaseMethod msig = Signature.ParseMethodDefSig(ttc.type.m, _DecodeSignature(tokens["S0"]));
            Metadata.MethodDefRow mdr = Metadata.GetMethodDef(ttc.type.m, tokens["M"], ttc.type, msig, ass);
            mtc.meth = mdr;
            mtc.msig = msig;
            return mtc;
        }

        private static Assembler.TypeToCompile _DemangleType(Dictionary<string, string> tokens, Assembler ass)
        {
            Metadata.TypeDefRow tdr = Metadata.GetTypeDef(tokens["m"], tokens["N"], tokens["T"], ass);
            byte[] sig = _DecodeSignature(tokens["S"]);
            int x = 0;
            Signature.Param tsig = Signature.ParseParamSig(tdr.m, sig, ref x);
            return new Assembler.TypeToCompile { tsig = tsig, type = tdr };
        }

        private static byte[] _DecodeSignature(string s)
        {
            List<byte> ret = new List<byte>();
            for (int i = 0; i < s.Length; i += 2)
            {
                string b_s = s.Substring(i, 2);
                byte b = Convert.ToByte(b_s, 16);
                ret.Add(b);
            }
            return ret.ToArray();
        }

        private static Dictionary<string, string> _SplitMangledName(string s)
        {
            int i = 0;
            Dictionary<string, string> ret = new Dictionary<string, string>();

            while (i < s.Length)
            {
                if (s[i] != '_')
                    break;
                i++;

                string tag = new string(new char[] { s[i] });
                i++;
                if (s[i] == 'p')
                {
                    ret.Add("Special", "");
                    i += 7;
                    continue;
                }
                List<char> c_length = new List<char>();
                while (s[i] != '_')
                {
                    c_length.Add(s[i]);
                    i++;
                }
                i++;
                int length = Int32.Parse(new string(c_length.ToArray()));

                List<char> c_value = new List<char>();
                while (length > 0)
                {
                    c_value.Add(s[i]);
                    i++;
                    length--;
                }

                string orig_tag = tag;
                int suffix = 0;
                while (ret.ContainsKey(tag))
                {
                    tag = orig_tag + suffix.ToString();
                    suffix++;
                }
                ret.Add(tag, new string(c_value.ToArray()));
            }

            return ret;
        }
    }
}
